package com.smart.dhu.study_recyclerview

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.LayoutManager
import androidx.recyclerview.widget.RecyclerView.Recycler
import com.smart.dhu.study_recyclerview.StackLayoutManager.OnStackListener
import kotlin.math.abs
import kotlin.math.floor

/**
 * @author david.long 2024/6/25 08:45
 */
class CoverFlowLayoutManager : LayoutManager() {

    private val TAG = "CoverFlowLayoutManager"

    private var pendingScrollPosition: Int = RecyclerView.NO_POSITION
    private var childWidth = 0
    /** 一次完整的聚焦滑动所需要移动的距离 */
    private var onceCompleteScrollLength = -1f
    /** 第一个item的偏移量 */
    private var firstChildCompleteScrollLength = -1f
    /** 屏幕可见的第一个item的position */
    private var firstVisibleItemPosition = 0
    /** 屏幕可见的最后一个item的position */
    private var lastVisibleItemPosition = 0
    /** item之间的间距 */
    private var normalViewGap = 0

    /** 水平方向累计偏移量 */
    private var horizontalOffset = 0f
    private var selectAnimator: ValueAnimator? = null

    override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
        return RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
    }

    override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
        if (pendingScrollPosition != RecyclerView.NO_POSITION) {
            if (state.itemCount == 0) {
                removeAndRecycleAllViews(recycler)
                return
            }
        }

        onceCompleteScrollLength = -1f

        detachAndScrapAttachedViews(recycler)

        if (onceCompleteScrollLength == -1f) {
            //初始化一些常量
            val itemView = recycler.getViewForPosition(0)
            measureChildWithMargins(itemView, 0, 0)
            childWidth = getDecoratedMeasurementHorizontal(itemView)
        }

        // 修正第一个可见view firstVisibleItemPosition 已经滑动了多少个完整的 onceCompleteScrollLength 就代表滑动了多少个item
        firstChildCompleteScrollLength = width / 2f + childWidth / 2f

        //重新布局
        fill(recycler, state, 0)
    }

    private fun fill(recycler: RecyclerView.Recycler, state: RecyclerView.State, dx: Int): Int {
        val resultDelta = fillHorizontal(recycler, state, dx)
        return resultDelta
    }

    private fun fillHorizontal(recycler: RecyclerView.Recycler, state: RecyclerView.State, dx: Int): Int {

        //边界检测
        var tempDx = dx
        if (dx < 0) {
            //已到达左边界
            if (horizontalOffset < 0) {
                horizontalOffset = 0f
                tempDx = 0
            }
        }

        if (dx > 0) {
            if (horizontalOffset >= getMaxOffset()) {
                horizontalOffset = getMaxOffset()
                tempDx = 0
            }
        }

        //分离全部的view，加入到临时缓存
        detachAndScrapAttachedViews(recycler)

        var startX: Float
        val fraction: Float
        if (horizontalOffset >= firstChildCompleteScrollLength) {
            //当第一个item不可见的时候
            startX = normalViewGap.toFloat()
            onceCompleteScrollLength = (childWidth + normalViewGap).toFloat()
            firstVisibleItemPosition = floor(Math.abs(horizontalOffset - firstChildCompleteScrollLength) / onceCompleteScrollLength).toInt() + 1
            fraction = (Math.abs(horizontalOffset - firstChildCompleteScrollLength) % onceCompleteScrollLength) / (onceCompleteScrollLength * 1.0f)
        } else {
            //当第一个item可见的时候
            startX = getMinOffset()
            firstVisibleItemPosition = 0
            onceCompleteScrollLength = firstChildCompleteScrollLength
            fraction = (Math.abs(horizontalOffset) % onceCompleteScrollLength) / (onceCompleteScrollLength * 1.0f)
        }
        // 临时将 lastVisibleItemPosition 赋值为getItemCount() - 1，放心，下面遍历时会判断view是否已溢出屏幕，并及时修正该值并结束布局
        lastVisibleItemPosition = itemCount - 1

        //每次布局之前都要对startX进行一次调整，初始的位置调整了，后面所有的view都是基于此位置进行布局的
        val normalViewOffset = onceCompleteScrollLength * fraction
        startX -= normalViewOffset

        for (i in firstVisibleItemPosition until lastVisibleItemPosition) {

            val item = recycler.getViewForPosition(i)
            //1.添加view
            addView(item)
            //2.测量view
            measureChildWithMargins(item, 0, 0)

            val l = startX.toInt()
            val t = paddingTop
            val r = l + getDecoratedMeasurementHorizontal(item)
            val b = t + getDecoratedMeasurementVertical(item)
            //3.布局view
            layoutChunk(item, l, t, r, b)

            startX += childWidth + normalViewGap

            //如果超出了边界就退出循环，不再添加view了
            if (startX > width - paddingRight) {
                lastVisibleItemPosition = i
                break
            }
        }

        return tempDx
    }

    private fun layoutChunk(item: View, l: Int, t: Int, r: Int, b: Int) {
        // 缩放子view
//        val minScale = 0.6f
        val minScale = 1f
        val currentScale: Float
        //中间item中心点位置横坐标
        val childCenterX = (r + l) / 2
        val parentCenterX = width / 2
        val isChildLayoutLeft = childCenterX <= parentCenterX
        if (isChildLayoutLeft) {
            //越往左边越小
            val fractionScale = (parentCenterX - childCenterX) / (parentCenterX * 1.0f)
            currentScale = 1.0f - (1.0f - minScale) * fractionScale
        } else {
            //越往右边越小
            val fractionScale = (childCenterX - parentCenterX) / (parentCenterX * 1.0f)
            currentScale = 1.0f - (1.0f - minScale) * fractionScale
        }
        item.scaleX = currentScale
        item.scaleY = currentScale
        item.setAlpha(currentScale)
        layoutDecoratedWithMargins(item, l, t, r, b)
    }

    /**
     * 获取某个childView在竖直方向所占的空间,将margin考虑进去
     */
    private fun getDecoratedMeasurementVertical(view: View): Int {
        val params = view.layoutParams as RecyclerView.LayoutParams
        return (getDecoratedMeasuredHeight(view) + params.topMargin
                + params.bottomMargin)
    }

    /**
     * 获取某个childView在水平方向所占的空间,将margin考虑进去
     */
    private fun getDecoratedMeasurementHorizontal(view: View): Int {
        val params = view.layoutParams as RecyclerView.LayoutParams
        return getDecoratedMeasuredWidth(view) + params.leftMargin + params.rightMargin
    }

    /**
     * 最小偏移量，中间的item距离最左边的距离
     */
    private fun getMinOffset(): Float {
        if (childWidth == 0) {
            return 0f
        }
        return (width - childWidth) / 2f
    }

    /**
     * 最大偏移量
     */
    private fun getMaxOffset(): Float {
        if (childWidth == 0 || itemCount == 0) return 0f
        return ((childWidth + normalViewGap) * (itemCount - 1)).toFloat()
    }

    override fun scrollToPosition(position: Int) {
        pendingScrollPosition = position
    }

    override fun onLayoutCompleted(state: RecyclerView.State) {
        pendingScrollPosition = RecyclerView.NO_POSITION
    }

    override fun canScrollHorizontally(): Boolean {
        return true
    }

    override fun scrollHorizontallyBy(
        dx: Int,
        recycler: RecyclerView.Recycler,
        state: RecyclerView.State
    ): Int {

        // 手指从右向左滑动，dx > 0; 手指从左向右滑动，dx < 0;
        // 位移0、没有子View 当然不移动
        if (dx == 0 || childCount == 0) {
            return 0
        }
        val realDx = dx / 1.0f
        if (abs(realDx) < 0.00000001f) {
            return 0
        }
        horizontalOffset += dx

        val tempDx = fill(recycler, state, dx)

        return tempDx
    }

    override fun onScrollStateChanged(state: Int) {
        super.onScrollStateChanged(state)

        when (state) {
            //当手指按下时，停止当前正在播放的动画
            RecyclerView.SCROLL_STATE_DRAGGING ->
                cancelAnimator()

            RecyclerView.SCROLL_STATE_IDLE -> {
                //当列表滚动停止后
                //找到离目标落点最近的item索引
                smoothScrollToPosition(findShouldSelectPosition(), null)
            }
        }
    }

    private fun findShouldSelectPosition(): Int {
        if (onceCompleteScrollLength == -1f || firstVisibleItemPosition == -1) {
            return -1
        }
        val position = (Math.abs(horizontalOffset) / (childWidth + normalViewGap))
        // 超过一半，应当选中下一项
        if ((Math.abs(horizontalOffset) % (childWidth + normalViewGap)) >= (childWidth + normalViewGap) / 2.0f) {
            if (position + 1 <= itemCount - 1) {
                return (position + 1).toInt()
            }
        }
        return position.toInt()
    }

    /**
     * 平滑滚动到某个位置
     *
     * @param position 目标Item索引
     */
    fun smoothScrollToPosition(position: Int, listener: OnStackListener?) {
        if (position > -1 && position < itemCount) {
            startValueAnimator(position, listener)
        }
    }

    /**
     * 取消动画
     */
    fun cancelAnimator() {
        if (selectAnimator != null && (selectAnimator?.isStarted == true || selectAnimator?.isRunning == true)) {
            selectAnimator?.cancel()
        }
    }

    private fun startValueAnimator(position: Int, listener: OnStackListener?) {
        cancelAnimator()

        val distance = getScrollToPositionOffset(position)

        val minDuration = 100
        val maxDuration = 300
        val duration: Long

        val distanceFraction = ((abs(distance.toDouble()) / (childWidth + normalViewGap)).toFloat())

        Log.i(TAG, "horizontalOffset-->$horizontalOffset, distance-->$distance, distanceFraction-->$distanceFraction")

        duration = if (distance <= (childWidth + normalViewGap)) {
            (minDuration + (maxDuration - minDuration) * distanceFraction).toLong()
        } else {
            (maxDuration * distanceFraction).toLong()
        }
        selectAnimator = ValueAnimator.ofFloat(0.0f, distance)
        selectAnimator?.setDuration(duration)
        selectAnimator?.interpolator = LinearInterpolator()
        val startedOffset = horizontalOffset
        selectAnimator?.addUpdateListener { animation ->
            val value = animation.animatedValue as Float
            horizontalOffset = startedOffset + value
            requestLayout()
        }
        selectAnimator?.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                listener?.onFocusAnimEnd()
            }
        })
        selectAnimator?.start()
    }

    /**
     * 滑动到指定的位置需要移动的距离
     * @param position
     * @return
     */
    private fun getScrollToPositionOffset(position: Int): Float {
        return position * (childWidth + normalViewGap) - Math.abs(horizontalOffset)
    }

}